//
//  ViewController.m
//  runtimeTest_isa_消息转发
//
//  Created by Song on 2018/4/24.
//  Copyright © 2018年 Song. All rights reserved.
//

#import "ViewController.h"
#import "MyClass.h"
#import <objc/runtime.h>
#import "Person.h"
#import "Person1.h"
#import "Animal.h"
#import "Sark.h"
#import "GSYClass.h"
#import "UIButton+Addition.h"

#import <objc/message.h>

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *testButton;
-(void)myTestPrint:(NSString *)str;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 关联对象
//    [self associatedObject];
    
    // 南峰子实例_类相关操作函数
//    [self nanFengZi];
    
    // 南峰子实例_成员变量与属性
//    [self variableAndProperty];
    
    // 南峰子实例_Method Swizzling
    [self methodSwizzling]; // load 类方法
    
    // runtime 题 (Sark)
//    [self runtimeTest];
    
    // 消息发送
//    [self myClassTest];
    
    // 消息转发
//    [self performSelector:@selector(myTestPrint:) withObject:@" = hello"]; // 未实现 myTestPrint 方法

}

// 消息转发 第一次补救 :在 resolveInstanceMethod: 方法中，利用 class_addMethod 方法，将未实现的 myTestPrint: 绑定到 myMethod 上就能完成转发，最后返回 YES
void myMethod(id self,SEL _cmd,NSString *nub) {
    NSLog(@"myMethod中的NSLog:%@",nub);
}

+(BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(myTestPrint:)) {
        class_addMethod([self class], sel, (IMP)myMethod, "v@:@");
//        class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
        return YES;
    }else {
        return [super resolveInstanceMethod:sel];
    }
}

// 消息转发 第二次补救 :这个方法要求返回一个 id。使用场景一般是将 A 类的某个方法，转发到 B 类的实现中去。
-(id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(myTestPrint:)) {
        return [Person new];
    }else {
        return [super forwardingTargetForSelector:aSelector];
    }
}

// 消息转发 第三次补救 :与第二次类似，但更灵活，forwardInvocation: 可以让我们转发到多个对象中去
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(myTestPrint:)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }else {
        return [super methodSignatureForSelector:aSelector];
    }
}

-(void)forwardInvocation:(NSInvocation *)anInvocation {
    Person1 *p1 = [Person1 new];
    Animal *an = [Animal new];
    if ([p1 respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:p1];
    }
    if ([an respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:an];
    }
}

// 消息发送
-(void)myClassTest {
    /*
     myClass 是实例对象
     MyClass 是类对象
     MyClass 的元类就是 NSObject 的元类
     NSObject 就是 Root class (class)
     NSObject 的 superclass 为 nil
     NSObject 的元类就是它自己 (Root class(meta)自己)
     NSObject 的元类的 superclass 就是 NSObject
     */
    
    MyClass *myClass = [[MyClass alloc] init];
    
//    [myClass printLog];
    ((void (*)(id, SEL))(void *) objc_msgSend)(myClass, @selector(printLog));
    
    Class classs = ((id (*)(id, SEL))(void *) objc_msgSend)(myClass, @selector(class));
    NSLog(@"=========== %p",classs);
    
    Class class = [myClass class]; // MyClass 的类对象
    Class metaClass = object_getClass(class); // 元类对象
    Class metaOfMetaClass = object_getClass(metaClass); // 元类对象的元类对象
    Class rootMetaClass = object_getClass(metaOfMetaClass); // 根元类对象
    
    Class superClass = class_getSuperclass(class); // MyClass 的父类
    Class superOfSuperClass = class_getSuperclass(superClass); // 父类的父类
    Class superOfMetaOfSuperclass = class_getSuperclass(object_getClass(superClass)); // 父类的元类的父类
    
    NSLog(@"MyClass 实例对象是：%p",myClass);
    NSLog(@"MyClass 类对象是：%p",class);
    NSLog(@"MyClass 元类对象是：%p",metaClass);
    NSLog(@"MyClass 元类对象的元类对象是：%p",metaOfMetaClass);
    NSLog(@"MyClass 根元类对象是：%p",rootMetaClass);
    NSLog(@"MyClass 父类是：%@",superClass);
    NSLog(@"MyClass 父类的父类是：%@",superOfSuperClass);
    NSLog(@"MyClass 父类的元类的父类是：%@",superOfMetaOfSuperclass);
    NSLog(@" ");
    NSLog(@"NSObject 元类对象是：%p",object_getClass([NSObject class]));
    NSLog(@"NSObject 父类是：%@",[[NSObject class] superclass]);
    NSLog(@"NSObject 元类对象的父类是：%@",[object_getClass([NSObject class]) superclass]);
    NSLog(@" ");
    
    
    BOOL res1 = class_isMetaClass(class); // 是否是元类 0
    BOOL res2 = class_isMetaClass(metaClass); // 是否是元类 1
    NSLog(@"类名：%s",class_getName(class)); // 获取类的类名
    NSLog(@"是否是元类:%d %d",res1,res2);
    NSLog(@"获取实例大小：%zu",class_getInstanceSize(class));
    
    unsigned int outCount = 0;
    // Ivar 是表示实例变量的类型，其实际是一个指向 objc_ivar 结构体的指针
    Ivar *ivars = class_copyIvarList(class, &outCount); // 获取整个成员变量列表
    for (int i = 0; i< outCount; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        const char *type = ivar_getTypeEncoding(ivar);
        NSLog(@"成员变量名：%s 成员变量类型：%s",name,type);
    }
    free(ivars); // 注意释放内存！
    
}

// runtime 题 (Sark)
-(void)runtimeTest {
    NSLog(@"%@",NSStringFromClass([self class]));
    NSLog(@"%@",NSStringFromClass([super class]));
    
    // Sark
    //    id cls = [Sark class];
    //    void *obj = &cls;
    //    // obj被转换成了一个指向Sark Class的指针，然后使用id转换成了objc_object类型。obj现在已经是一个Sark类型的实例对象了，可以调用 speak 方法
    //    [(__bridge id)obj speak];
    
    NSLog(@"ViewController = %@ , 地址 = %p", self, &self);
    id cls = [Sark class];
    NSLog(@"Sark class = %@ 地址 = %p", cls, &cls);
    void *obj = &cls;
    NSLog(@"Void *obj = %@ 地址 = %p", obj,&obj);
    [(__bridge id)obj speak];
    Sark *sark = [[Sark alloc]init];
    NSLog(@"Sark instance = %@ 地址 = %p",sark,&sark);
    [sark speak];
}

// 南峰子实例_类相关操作函数
-(void)nanFengZi {
    GSYClass *gsyClass = [[GSYClass alloc] init];
    unsigned int outCount = 0;
    Class cls = gsyClass.class;
    
    // 类名
    NSLog(@"class name: %s",class_getName(cls));
    NSLog(@"==========================================================");
    
    // 父类
    NSLog(@"super class name: %s",class_getName(class_getSuperclass(cls)));
    NSLog(@"==========================================================");
    
    // 是否是元类
    NSLog(@"GSYClass is %@ a meta-class",(class_isMetaClass(cls) ? @"" : @"not"));
    NSLog(@"==========================================================");
    Class meta_class = objc_getMetaClass(class_getName(cls));
    NSLog(@"%s`s meta-class is %@ a meta-class,name is %s",class_getName(cls),(class_isMetaClass(meta_class) ? @"" : @"not"),class_getName(meta_class));
    NSLog(@"==========================================================");
    
    // 变量实例大小
    NSLog(@"instance size: %zu", class_getInstanceSize(cls));
    NSLog(@"==========================================================");
    
    // 成员变量
    Ivar *ivars = class_copyIvarList(cls, &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivars[i];
        const char *type = ivar_getTypeEncoding(ivar);
        NSLog(@"instance variable's name: %s, type: %s, at index: %d", ivar_getName(ivar),type, i);
    }
    free(ivars);
    
    Ivar string = class_getInstanceVariable(cls, "_array"); // 获取类中指定名称实例成员变量的信息
    if (string != NULL) {
        NSLog(@"instace variable %s", ivar_getName(string));
    }
    NSLog(@"==========================================================");
    
    // 属性操作
    objc_property_t *properties = class_copyPropertyList(cls, &outCount);
    for (int i = 0; i<outCount; i++) {
        objc_property_t property = properties[i];
        const char *name = property_getName(property);
        const char *att = property_getAttributes(property);
        NSLog(@"property`s name: %s , attributes: %s",name,att);
    }
    free(properties);
    
    objc_property_t array = class_getProperty(cls, "array"); // 获取指定的属性
    if (array != NULL) {
        NSLog(@"property %s",property_getName(array));
    }
    NSLog(@"==========================================================");
    
    // 方法操作
    Method *methods = class_copyMethodList(cls, &outCount);
    for (int i = 0; i<outCount ; i++) {
        Method method = methods[i];
        NSLog(@"method`s name: %s",method_getName(method));
    }
    free(methods);
    
    Method method1 = class_getInstanceMethod(cls, @selector(method1)); // 获取对象方法 / 实例方法
    if (method1 != NULL) {
        NSLog(@"instance method: %s",method_getName(method1));
    }
    
    Method classMethod = class_getClassMethod(cls, @selector(classMethod1));
    if (classMethod != NULL) {
        NSLog(@"class method: %s",method_getName(classMethod));
    }
    
    NSLog(@"GSYClass is%@ responsed to selector: method3WithArg1:arg2:",class_respondsToSelector(cls, @selector(method3WithArg1:arg2:)) ? @"" : @"not");
    
    IMP imp = class_getMethodImplementation(cls, @selector(method1));
    imp();
    NSLog(@"==========================================================");
    
    // 协议
    Protocol * __unsafe_unretained * protocols = class_copyProtocolList(cls, &outCount);
    Protocol *protocol;
    for (int i = 0; i<outCount; i++) {
        protocol = protocols[i];
        NSLog(@"protocol name: %s",protocol_getName(protocol));
    }
    NSLog(@"GSYClass is%@ responsed to protocol %s",class_conformsToProtocol(cls, protocol) ? @"" : @"not", protocol_getName(protocol));
    NSLog(@"==========================================================");
}

// 南峰子实例_成员变量与属性
-(void)variableAndProperty {
    float a[] = {1.1,2.2,3.3,4.4};
    NSLog(@"array encoding type: %s",@encode(typeof(a)));
}

// 关联对象
-(void)associatedObject{
    // 关联对象 - 方法
    [self.testButton handleClickCallBack:^(UIButton *button) {
        NSLog(@" - click - button - ");
    }];
    
    // 关联对象 - 属性
    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    btn.backgroundColor = [UIColor redColor];
    btn.block = ^(UIButton *button){
        NSLog(@"点 block ======= ");
    };
    [self.view addSubview:btn];
}


// 南峰子实例_Method Swizzling
-(void)methodSwizzling {
    /*
     两个方法的实现交换，即由原来的A－AImp、B－BImp对应关系变成了A－BImp、B－AImp。
     交换前：Asel－>AImp Bsel－>BImp
     交换后：Asel－>BImp Bsel－>AImp
     */
}
+(void)load { // Swizzling应该总是在+load中执行，这样可以保证方法一定会调用且不会出现异常。
    static dispatch_once_t onceToken; // Swizzling应该总是在dispatch_once中执行，这样可以保证只运行一次。
    /*
     因为swizzling会改变全局状态，所以我们需要在运行时采取一些预防措施。原子性就是这样一种措施，它确保代码只被执行一次，不管有多少个线程。
     */
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        NSLog(@"class = %@",class);
        
        SEL originalSelector = @selector(viewWillAppear:); // 原来的选择器
        SEL swizzledSelector = @selector(xxx_viewWillAppear:); // 新的选择器
        
        // 通过SEL获取一个方法 class_getInstanceMethod
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        // 获取一个方法的实现 method_getImplementation
        // 获取一个OC实现的编码类型 method_getTypeEncoding
        // 給方法添加实现 class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); // 先尝试給源方法添加实现，这里是为了避免源方法没有实现的情况
        
        if (didAddMethod) {
            NSLog(@"添加成功");
            // 添加成功：将源方法的实现替换到交换方法的实现
            // 用一个方法的实现替换另一个方法的实现 class_replaceMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            NSLog(@"添加失败");
            // 添加失败：说明源方法已经有实现，直接将两个方法的实现交换即可
            // 交换两个方法的实现 method_exchangeImplementations
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}
-(void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"view will appear : %@",self);
}


@end
